import isa
from assembler import assemblers
from scanner import *

class Compiler:
    def scann(self, path):
        with open(path, 'r') as file:
            current_line = 0
            label_dict = {}
            instrs : list[list[Token]] = []
            lines = file.readlines()

            for line in lines:
                line = line.strip()
                if len(line) == 0 or line[0] == '#':
                    continue

                # a label
                if line[0] == '.':
                    label_dict[line[1 : -1]] = current_line
                # a instruction
                else:
                    sanner = Sanner(line)
                    instrs.append(sanner.scan())
                    current_line += 1
            
            # replace all labels
            for i, instr in enumerate(instrs):
                # b-type command
                if instr[0].token_type == TokenType.BTYPE_COMMAND:
                    instr[5] = Token(TokenType.NUMBER, label_dict[instr[5].context] - i)

                # j-type command
                if instr[0].token_type == TokenType.JTYPE_COMMAND:
                    instr[3] = Token(TokenType.NUMBER, label_dict[instr[3].context] - i)

            return instrs

    def assemble(self, instrs : list[list[Token]]):
        with open('program.bin', 'wb') as file:
            file.write(int(0).to_bytes(4, 'little'))
            for instr in instrs:
                # first token must be a command
                command_token = instr[0]
                if command_token.token_type in assemblers == False:
                    print("[error] a instuction must begin with a command!")
                    return
                else:
                    try:
                        value = assemblers[command_token.token_type].assemble(instr)
                        file.write(value.to_bytes(4, 'little'))
                    except Exception as e:
                        print(e)
                        break

    def compile(self, path):
        instrs = self.scann(path)
        self.assemble(instrs)
            